# 修改纪要：
# 1. 加入避让作业逻辑，向正面吊与轨道吊类中加入：（1）避让作业事件（2）避让作业进程（3）避让位置属性 (完成)
# 2. 向调度作业管理器中加入等待、避让调度机制，依据作业优先级进行调度
# 3. 修改正面吊的移动逻辑，跨线移动时，需要先移动到堆场最右侧，再移动到另外一条作业线，再移动到目标位置
# 4. 每次移动完成都要进行一次调度
# 5. 差速超越问题与安全距离问题已解决
# 模型结构:
# 正面吊模块<-调度作业<-作业调度控制器模块->调度作业->轨道吊模块
# 调度节点：每次设备移动完成后进行一次调度（事件驱动）， 即对轨道吊与正面吊的动作命令进行下发
# （三种情况：对设备下发新任务、无操作、中断设备当前的移动动作进行等待或避让、恢复移动作业）
# 设备动作：等待、空载移动+抓取、重载移动+放置、避让移动、中断动作

"调度前一定设备先停下来！！！！！！！"
# 调度规则：
# 1. 完成移动作业需要进行调度的设备称为主调度设备，另一设备称为被调度设备；
# 2. 当位于同一作业状态下，轨道吊的作业优先级高于正面吊；
# 3. 当主调度设备调度优先级较高，且主调度设备与被调度设备的作业区间不重叠时，主调度设备直接执行移动作业；
# 4. 当主调度设备调度优先级较高，且主调度设备与被调度设备的作业区间重叠时，主调度设备直接执行移动作业，中断被调度设备当前作业，进行避让操作；
# 5. 当主调度设备调度优先级较低，且主调度设备与被调度设备的作业区间不重叠时，主调度设备直接执行移动作业；
# 6. 当主调度设备调度优先级较低，且主调度设备与被调度设备的作业区间重叠时，主调度设备根据具体情况，执行等待、避让操作。
# 占用状态: 0无集装箱，1有集装箱
import simpy
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import pandas as pd
import copy
import time
import enum
import matplotlib.colors as mcolors
from sklearn.preprocessing import MinMaxScaler

# 状态定义：
# -2. 无任务空闲状态 / 无法作业状态
# -1. 避让状态
# 0. 有任务空闲状态
# 1. 空载移动状态
# 2. 抓取状态
# 3. 重载移动状态
# 4. 放置状态
# 5. 换线状态

# **********************************************加一个根据距离调整优先级的机制
PRIORITY = [-2, -1, 0, 5, 1, 3, 2, 4]
SafetyDistance = 1
# 环境定义
env = simpy.Environment()
# 全局事件定义
SimulationStopEvent = env.event()
ShaduleEvent = env.event()
# 轨道吊任务队列
# CraneTaskQueue =[{'StartPostion': [2,7,2], 'EndPostion': [15,7,1]}]
# ReachStackerTaskQueue =[{'StartPostion': [2,8,1], 'EndPostion': [15,9,1]}]
CraneTaskQueue = [{'StartPostion': [40, 4, 1], 'EndPostion': [54, 6, 1]}, {'StartPostion': [44, 2, 1], 'EndPostion': [26, 2, 1]}, {'StartPostion': [45, 8, 2], 'EndPostion': [4, 10, 2]}, {'StartPostion': [37, 2, 2], 'EndPostion': [36, 13, 1]}, {'StartPostion': [53, 11, 1], 'EndPostion': [22, 12, 1]}, {'StartPostion': [31, 1, 2], 'EndPostion': [53, 5, 1]}, {'StartPostion': [16, 3, 1], 'EndPostion': [9, 1, 1]}, {'StartPostion': [11, 10, 1], 'EndPostion': [43, 14, 1]}, {'StartPostion': [8, 10, 2], 'EndPostion': [22, 2, 1]}, {'StartPostion': [2, 12, 2], 'EndPostion': [36, 11, 2]}, {'StartPostion': [3, 1, 1], 'EndPostion': [41, 5, 1]}, {'StartPostion': [28, 3, 1], 'EndPostion': [41, 13, 1]}, {'StartPostion': [19, 1, 2], 'EndPostion': [51, 1, 1]}]
ReachStackerTaskQueue = [{'StartPostion': [36, 12, 1], 'EndPostion': [19, 6, 1]}, {'StartPostion': [54, 6, 1], 'EndPostion': [64, 8, 2]}, {'StartPostion': [7, 8, 2], 'EndPostion': [38, 13, 1]}, {'StartPostion': [47, 8, 1], 'EndPostion': [57, 5, 1]}, {'StartPostion': [37, 9, 2], 'EndPostion': [24, 13, 1]}, {'StartPostion': [27, 10, 2], 'EndPostion': [22, 14, 1]}, {'StartPostion': [10, 11, 1], 'EndPostion': [48, 7, 1]}]

"----------------------------工具函数-----------------------------"


def Get_MoveRange(*args: list) -> np.ndarray:
    if len(args) == 1:
        Position = args[0]
        return np.arange(Position[0] - SafetyDistance, Position[0] + 1 + SafetyDistance)
    if len(args) == 2:
        StartPoint = args[0]
        EndPoint = args[1]
        # 判断起点和终点的位置关系
        if StartPoint[0] == EndPoint[0]:
            return np.arange(StartPoint[0] - SafetyDistance, EndPoint[0] + 1 + SafetyDistance)
        elif StartPoint[0] < EndPoint[0]:
            return np.arange(StartPoint[0] - SafetyDistance, EndPoint[0] + 1 + SafetyDistance)
        else:
            return np.arange(StartPoint[0] + SafetyDistance, EndPoint[0] - 1 - SafetyDistance, -1)


"----------------------------绘图类-----------------------------"


class DrawPicture:
    ContainerChangeLocationList = []
    ContainerChangeLocationTimeList = []

    def __init__(self):
        self.ani = None
        self.DeviceSC = None
        self.ContainerSC1 = None
        self.ContainerSC2 = None
        self.ChangeDict = {}
        plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
        plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号
        self.fig = plt.figure()
        self.ax = self.fig.add_subplot(111)
        self.text = self.ax.text(0, 14, "时间点: %d" % 0)
        self.ax.set_xlim(1, 66)
        self.ax.set_ylim(1, 14)
        self.xtick = range(0, 66)
        self.ytick = range(0, 15)
        self.ax.set_xticks(self.xtick)
        self.ax.set_yticks(self.ytick)
        self.ax.grid(True)
        self.ax.set_aspect(1)
        self.ax.set_xlabel("贝位")
        self.ax.set_ylabel("栈位")
        self.ax.set_title("集装箱堆场")
        self.Locationlist1 = []
        self.Locationlist2 = []
        self.StateColor = {-2: 'yellow', -1: 'blue', 0: 'yellow', 1: 'green', 2: 'black', 3: 'red', 4: 'black',
                           5: 'aquamarine'}
        # self.init_containerlocation()

    def LoadData(self, loclist1, loclist2, ContainerLocationInit):
        self.ChangeDict = dict(
            zip(DrawPicture.ContainerChangeLocationTimeList, DrawPicture.ContainerChangeLocationList))
        self.Locationlist1 = loclist1
        self.Locationlist2 = loclist2
        # 寻找一层集装箱位置
        self.ContainerLocationList = ContainerLocationInit
        # 创建一个 66*15 的网格
        x = np.arange(1, 67)
        y = np.arange(1, 15)
        X, Y = np.meshgrid(x, y)
        # 集装箱散点图一层图层
        self.ContainerSC1 = self.ax.scatter([], [], s=100, marker='s', alpha=1, c='orange')
        self.ContainerSC2 = self.ax.scatter([], [], s=100, marker='s', alpha=1, c='brown')
        # 查找一层现有集装箱位置
        index = np.where(ContainerLocationInit[0][:][:] == 1)
        indices_to_remove = list(zip(index[0], index[1]))
        # 创建布尔索引
        mask = np.zeros(X.shape, dtype=bool)
        for index in indices_to_remove:
            mask[index[0], index[1]] = True
        # 应用布尔索引移除值
        X_masked = X[mask]
        Y_masked = Y[mask]
        # 将网格展平为一维数组
        x_flattened = X_masked.flatten()
        y_flattened = Y_masked.flatten()
        self.ContainerSC1.set_offsets(np.c_[x_flattened, y_flattened])
        # 查找二层现有集装箱位置
        index = np.where(ContainerLocationInit[1][:][:] == 1)
        indices_to_remove = list(zip(index[0], index[1]))
        # 创建布尔索引
        mask = np.zeros(X.shape, dtype=bool)
        for index in indices_to_remove:
            mask[index[0], index[1]] = True
        # 应用布尔索引移除值
        X_masked = X[mask]
        Y_masked = Y[mask]
        # 将网格展平为一维数组
        x_flattened = X_masked.flatten()
        y_flattened = Y_masked.flatten()
        self.ContainerSC2.set_offsets(np.c_[x_flattened, y_flattened])
        # 设备散点图图层
        self.DeviceSC = self.ax.scatter([], [], s=70, marker='s')
        self.ani = FuncAnimation(self.fig, self.update, frames=len(self.Locationlist1) + 1, interval=10, blit=False,
                                 repeat=False)  # 创建动画效果

    def update(self, frame):
        # 时间点提示向左偏移
        if self.Locationlist1[frame][3] in self.ChangeDict:
            x = np.arange(1, 67)
            y = np.arange(1, 15)
            X, Y = np.meshgrid(x, y)
            # 查找一层现有集装箱位置
            index = np.where(self.ChangeDict[self.Locationlist1[frame][3]][0][:][:] == 1)
            indices_to_remove = list(zip(index[0], index[1]))
            # 创建布尔索引
            mask = np.zeros(X.shape, dtype=bool)
            for index in indices_to_remove:
                mask[index[0], index[1]] = True
            # 应用布尔索引移除值
            X_masked = X[mask]
            Y_masked = Y[mask]
            # 将网格展平为一维数组
            x_flattened = X_masked.flatten()
            y_flattened = Y_masked.flatten()
            self.ContainerSC1.set_offsets(np.c_[x_flattened, y_flattened])
            # 查找二层现有集装箱位置
            index = np.where(self.ChangeDict[self.Locationlist1[frame][3]][1][:][:] == 1)
            indices_to_remove = list(zip(index[0], index[1]))
            # 创建布尔索引
            mask = np.zeros(X.shape, dtype=bool)
            for index in indices_to_remove:
                mask[index[0], index[1]] = True
            # 应用布尔索引移除值
            X_masked = X[mask]
            Y_masked = Y[mask]
            # 将网格展平为一维数组
            x_flattened = X_masked.flatten()
            y_flattened = Y_masked.flatten()
            self.ContainerSC2.set_offsets(np.c_[x_flattened, y_flattened])
        self.text.set_text("时间点: %d" % self.Locationlist1[frame][3])
        x1 = self.Locationlist1[frame][0]
        y1 = self.Locationlist1[frame][1]
        Point1color = self.StateColor[self.Locationlist1[frame][2]]
        Point1color = mcolors.to_rgba(Point1color)
        x2 = self.Locationlist2[frame][0]
        y2 = self.Locationlist2[frame][1]
        Point2color = self.StateColor[self.Locationlist2[frame][2]]
        Point2color = mcolors.to_rgba(Point2color)
        self.DeviceSC.set_offsets([[x1, y1], [x2, y2]])
        self.DeviceSC.set_facecolors([Point1color, Point2color])


"----------------------------枚举类-----------------------------"


class WorkLinePos(enum.Enum):
    Up = 1
    Down = 2


"----------------------------集装箱箱位类-----------------------------"


class ContainerLocation:
    ContainerList = []
    ContainerListCopy = []

    def __init__(self, bay, stack, level, state, Locationtype):
        self.bay = bay  # 贝位
        self.level = level  # 层位
        self.stack = stack  # 栈位
        self.state = state  # 集装箱箱位状态: 放置货物 1 没有货物 0
        self.Locationtype = Locationtype  # 箱位类型: 0 普通箱位 1 道路箱位
        ContainerLocation.ContainerList.append(self)

    # 根据箱位号查询箱位对象列表
    @classmethod
    def FindContainerLocation(cls, bay, stack, level):
        return [x for x in cls.ContainerList if (x.stack == stack and x.level == level and x.bay == bay)]

    @classmethod
    def ContainerLocationListSave(cls):
        cls.ContainerListCopy.clear()
        cls.ContainerListCopy = copy.deepcopy(cls.ContainerList)
        return True

    @classmethod
    def ContainerLocationListRecover(cls):
        cls.ContainerList.clear()
        cls.ContainerList = copy.deepcopy(cls.ContainerListCopy)
        return True

    # 判断集装箱位置是否可抓取
    def CheckContainerLocationPick(self):
        # 如果当前货位上有货物，进一步判断
        if self.state == 1:
            # 如果当前货位处于二层，可以抓取
            if self.level == 2:
                return True
            # 如果当前货位处于一层，判断二层是否有货物
            elif self.level == 1:
                try:
                    # 获取二层货位对象
                    ContainerLocationSelect = ContainerLocation.FindContainerLocation(self.bay, self.stack,
                                                                                      self.level + 1).pop()
                except IndexError:
                    return True
                # 如果二层货位上有货物不可抓取，否则可以抓取
                if ContainerLocationSelect.state == 1:
                    return False
                else:
                    return True
        # 如果当前货位上没有货物，不可抓取
        else:
            return False

    def CheckContainerLocationPut(self):
        # 如果当前货位上没有货物，进一步判断
        if self.state == 0:
            # 如果当前货位处于一层，可以放置
            if self.level == 1:
                return True
            # 如果当前货位处于二层，判断一层是否有货物
            elif self.level == 2:
                # 获取二层货位对象
                ContainerLocationSelect = ContainerLocation.FindContainerLocation(self.bay, self.stack,
                                                                                  self.level - 1).pop()
                # 如果一层货位上有货物则可以放置，否则不可放置
                if ContainerLocationSelect.state == 1:
                    return True
                else:
                    return False
        # 如果当前货位上有货物，不可放置
        else:
            return False

    # 计算正面吊可以停靠的位置
    def DockingLocationSearch(self):
        TargetBay = self.bay
        # 获得目标位置的贝位和栈位
        TargetStack = self.stack
        # 栈位超出范围，无解，给出异常
        assert 3 < TargetStack < 15
        # 通道规则
        if TargetStack == 4:
            return [None, [TargetBay, 5]]
        if TargetStack == 5:
            return [None, [TargetBay, 6]]
        if TargetStack == 6:
            return [None, [TargetBay, 5]]
        if TargetStack == 13:
            return [[TargetBay, 14], None]
        if TargetStack == 14:
            return [[TargetBay, 13], None]
        MovePathUp = []
        MovePathDown = []
        CheckStack = TargetStack
        while True:
            # 向上侧查找，先判断是否为道路,若为道路,则将检查位置加入路径，退出
            CheckStack += 1
            ContainerLocationSelect = ContainerLocation.FindContainerLocation(TargetBay, CheckStack, 1).pop()
            if ContainerLocationSelect.Locationtype == 1:
                MovePathUp.append(CheckStack)
                break
            # 若不为道路，则检查当前位置一层是否有集装箱，若有则不能成为可用路径
            else:
                if ContainerLocationSelect.state == 0:
                    MovePathUp.append(CheckStack)
                    continue
                else:
                    # 判断是否为目标栈位的邻接栈位，若是则检查二层是否有集装箱，若没有则继续搜索，否则直接退出
                    if abs(CheckStack - TargetStack) == 1:
                        if ContainerLocation.FindContainerLocation(TargetBay, CheckStack, 2).pop().state <= \
                                ContainerLocation.FindContainerLocation(TargetBay, TargetStack, 2).pop().state:
                            continue
                        else:
                            MovePathUp.clear()
                            break
                    else:
                        MovePathUp.clear()
                        break
        CheckStack = TargetStack
        while True:
            # 向下侧查找，先判断是否为道路,若为道路,则将检查位置加入路径，退出
            CheckStack -= 1
            ContainerLocationSelect = ContainerLocation.FindContainerLocation(TargetBay, CheckStack, 1).pop()
            if ContainerLocationSelect.Locationtype == 1:
                MovePathDown.append(CheckStack)
                break
            # 若不为道路，则检查当前位置一层是否有集装箱，若有则不能成为可用路径
            else:
                if ContainerLocationSelect.state == 0:
                    MovePathDown.append(CheckStack)
                    continue
                else:
                    # 判断是否为目标栈位的邻接栈位，若是则检查二层是否有集装箱，若没有则继续搜索，否则直接退出
                    if abs(CheckStack - TargetStack) == 1:
                        if ContainerLocation.FindContainerLocation(TargetBay, CheckStack, 2).pop().state <= \
                                ContainerLocation.FindContainerLocation(TargetBay, TargetStack, 2).pop().state:
                            continue
                        else:
                            MovePathDown.clear()
                            break
                    else:
                        MovePathDown.clear()
                        break
        if len(MovePathUp) != 0 or len(MovePathDown) != 0:
            if len(MovePathUp) != 0:
                MovePathUp = np.array(MovePathUp)
                DockingLocationUp = [TargetBay, np.min(MovePathUp)]
            else:
                DockingLocationUp = None
            if len(MovePathDown) != 0:
                MovePathDown = np.array(MovePathDown)
                DockingLocationDown = [TargetBay, np.max(MovePathDown)]
            else:
                DockingLocationDown = None
            return [DockingLocationUp, DockingLocationDown]
        else:
            return False

    def PlacingFeasibilityQueries(self):
        TargetBay = self.bay
        TargetStack = self.stack
        if TargetStack < 7 or TargetStack > 12:
            return True
        else:
            # 初始化集装箱位置列表
            ContainerLocationInit = []
            for stack in range(7, 13):
                ContainerLocationInit.append(ContainerLocation.FindContainerLocation(TargetBay, stack, 1).pop())
            # 记录活动集装箱位置（即正面吊所涉及的所有集装箱位置）
            ActiveContainerLocation = []
            for item in ReachStackerTaskQueue:
                for key, value in item.items():
                    selectselectContainerLocation = ContainerLocation.FindContainerLocation(value[0], value[1], 1).pop()
                    if (selectselectContainerLocation.state == 1 and key == "StartPostion") or key == "EndPostion":
                        ActiveContainerLocation.append(
                            ContainerLocation.FindContainerLocation(value[0], value[1], 1).pop())
                    else:
                        continue
            # 删除活动集装箱位置，剩下的即为固定集装箱位置（即正面吊不涉及的集装箱位置）
            for item in ActiveContainerLocation:
                try:
                    ContainerLocationInit.remove(item)
                except ValueError:
                    continue
            FixedContainerPosition = ContainerLocationInit.copy()
            TargetBay = self.bay
            TargetStack = self.stack
            CheckStack = TargetStack
            FixContainerexistFlag = False
            ActiveContainerexistFlag = False
            while True:
                # 向上侧查找，先判断是否为道路,若为道路,则将检查位置加入路径，退出
                CheckStack += 1
                ContainerLocationSelect = ContainerLocation.FindContainerLocation(TargetBay, CheckStack, 1).pop()
                if ContainerLocationSelect.Locationtype == 1:
                    break
                else:
                    if ContainerLocationSelect in FixedContainerPosition:
                        FixContainerexistFlag = True
                    if ContainerLocationSelect in ActiveContainerLocation:
                        ActiveContainerexistFlag = True
            if FixContainerexistFlag == True and ActiveContainerexistFlag == True:
                return False
            else:
                pass
            FixContainerexistFlag = False
            ActiveContainerexistFlag = False
            CheckStack = TargetStack
            while True:
                # 向下侧查找，先判断是否为道路,若为道路则退出
                CheckStack -= 1
                ContainerLocationSelect = ContainerLocation.FindContainerLocation(TargetBay, CheckStack, 1).pop()
                if ContainerLocationSelect.Locationtype == 1:
                    break
                else:
                    if ContainerLocationSelect in FixedContainerPosition:
                        FixContainerexistFlag = True
                    if ContainerLocationSelect in ActiveContainerLocation:
                        ActiveContainerexistFlag = True
            if FixContainerexistFlag == True and ActiveContainerexistFlag == True:
                return False
            else:
                return True


"----------------------------调度控制器类-----------------------------"


class ControllStation:
    DataStack = []

    def __init__(self, env):
        self.env = env
        self.env.process(self.main())
        self.env.process(self.running())
        self.ShaduleDevice = []

    def running(self):
        yield SimulationStopEvent
        return True

    @staticmethod
    # 作业调度函数
    def Schedule(Device):
        """-----------------------------------标志位---------------------------------------"""
        # 优先级标志位
        PriorityFlag = False
        # 作业交叉标志位
        CrossFlag = False
        # 主调度设备可作业标志位
        MainShaduleDeviceWorkFeasibility = True
        """-----------------------------------中间变量--------------------------------------"""
        # 调度数据栈
        ScheduleDataStack = []

        # 指定主调度设备与被调度设备
        MainShaduleDevice = Device
        BeShaduleDevice = ReachStacker if Device == Crane else Crane
        # 获取主调度设备基本作业信息
        MainShaduleDeviceStateCode = MainShaduleDevice.StateCode
        MainShaduleDeviceCurrentPos = MainShaduleDevice.CurrentPos
        MainShaduleDeviceCurrentTask = MainShaduleDevice.CurrentTask
        MainShaduleDeviceTargetPos = MainShaduleDevice.TargetPos
        MainShaduleDevicePriority = PRIORITY.index(MainShaduleDeviceStateCode)
        # 获取被调度设备基本作业信息
        BeShaduleDeviceStateCode = BeShaduleDevice.StateCode
        BeShaduleDeviceCurrentPos = BeShaduleDevice.CurrentPos
        BeShaduleDeviceCurrentTask = BeShaduleDevice.CurrentTask
        BeShaduleDeviceTargetPos = BeShaduleDevice.TargetPos
        BeShaduleDevicePriority = PRIORITY.index(BeShaduleDeviceStateCode)

        """----------------------------------------作业可行性判断----------------------------------"""
        # 判断主调度设备是否为正面吊
        if MainShaduleDevice is ReachStacker:
            # 判断正面吊作业的作业状态
            if not MainShaduleDevice.LoadOn:
                MainShaduleDevice.TargetPos = MainShaduleDevice.CurrentTask["StartPostion"]
                # 判断该位置是否能抓取
                PickUPContainerLocation = ContainerLocation.FindContainerLocation(MainShaduleDevice.TargetPos[0],
                                                                                  MainShaduleDevice.TargetPos[1],
                                                                                  MainShaduleDevice.TargetPos[2]).pop()
                # 若该位置不能抓取，等待
                if not PickUPContainerLocation.CheckContainerLocationPick():
                    # 主调度作业设备不可作业
                    MainShaduleDevice.StateCode = -2
                    MainShaduleDeviceWorkFeasibility = False
                else:
                    # 主调度作业设备可作业
                    MainShaduleDevice.StateCode = 0
                    MainShaduleDeviceWorkFeasibility = True
            else:
                MainShaduleDevice.TargetPos = MainShaduleDevice.CurrentTask["EndPostion"]
                # 判断该位置是否能放置
                PutContainerLocation = ContainerLocation.FindContainerLocation(MainShaduleDevice.TargetPos[0],
                                                                               MainShaduleDevice.TargetPos[1],
                                                                               MainShaduleDevice.TargetPos[2]).pop()
                # 若该位置不能放置，等待
                if not PutContainerLocation.CheckContainerLocationPut():
                    # 主调度作业设备不可作业
                    MainShaduleDevice.StateCode = -2
                    MainShaduleDeviceWorkFeasibility = False
                else:
                    # 主调度作业设备可作业
                    MainShaduleDevice.StateCode = 0
                    MainShaduleDeviceWorkFeasibility = True
            # 获得正面吊的停靠位置
            DockingLocation = ContainerLocation.FindContainerLocation(MainShaduleDevice.TargetPos[0],
                                                                      MainShaduleDevice.TargetPos[1],
                                                                      MainShaduleDevice.TargetPos[
                                                                          2]).pop().DockingLocationSearch()
            # 无停靠位置，等待or避让
            if DockingLocation is False:
                # 主调度作业设备不可作业
                MainShaduleDevice.StateCode = -2
                MainShaduleDeviceWorkFeasibility = False
            else:
                # 主调度作业设备可作业
                MainShaduleDevice.StateCode = 0
                MainShaduleDeviceWorkFeasibility = True

            # 有停靠位置
            if MainShaduleDevice.Worklinepos == WorkLinePos.Down and DockingLocation is not False:
                DockingLocation_ = DockingLocation[1]
                if DockingLocation_ is None:
                    ControllStation.DataStack.append(DockingLocation[0])
                    MainShaduleDevice.ReplaceWorkLineEvent.succeed()
                    MainShaduleDevice.ReplaceWorkLineEvent = MainShaduleDevice.env.event()
                    return True
                else:
                    MainShaduleDevice.TargetPos = DockingLocation_
                    MainShaduleDeviceTargetPos = DockingLocation_

            elif MainShaduleDevice.Worklinepos == WorkLinePos.Up and DockingLocation is not False:
                DockingLocation_ = DockingLocation[0]
                if DockingLocation_ is None:
                    ControllStation.DataStack.append(DockingLocation[1])
                    MainShaduleDevice.ReplaceWorkLineEvent.succeed()
                    MainShaduleDevice.ReplaceWorkLineEvent = MainShaduleDevice.env.event()
                    return True
                else:
                    MainShaduleDevice.TargetPos = DockingLocation_
                    MainShaduleDeviceTargetPos = DockingLocation_
        # 判断主调度设备是否为轨道吊
        else:
            # 轨道吊是否处于空载作业状态
            if not MainShaduleDevice.LoadOn:
                MainShaduleDeviceTargetPos = MainShaduleDevice.CurrentTask["StartPostion"]
                MainShaduleDevice.TargetPos = MainShaduleDevice.CurrentTask["StartPostion"]
                # 判断该位置是否能抓取
                PickUPContainerLocation = ContainerLocation.FindContainerLocation(MainShaduleDeviceTargetPos[0],
                                                                                  MainShaduleDeviceTargetPos[1],
                                                                                  MainShaduleDeviceTargetPos[2]).pop()
                # 若该位置不能抓取，等待
                if not PickUPContainerLocation.CheckContainerLocationPick():
                    # 将主调度设备调度状态改为避让状态
                    MainShaduleDevice.StateCode = -2
                    MainShaduleDeviceWorkFeasibility = False
                else:
                    # 恢复主调度设备作业状态
                    MainShaduleDevice.StateCode = 0
                    MainShaduleDeviceWorkFeasibility = True

            else:
                MainShaduleDeviceTargetPos = MainShaduleDevice.CurrentTask["EndPostion"]
                MainShaduleDevice.TargetPos = MainShaduleDevice.CurrentTask["EndPostion"]
                # 判断该位置是否能放置
                PutContainerLocation = ContainerLocation.FindContainerLocation(MainShaduleDeviceTargetPos[0],
                                                                               MainShaduleDeviceTargetPos[1],
                                                                               MainShaduleDeviceTargetPos[2]).pop()

                if not PutContainerLocation.PlacingFeasibilityQueries() or not PutContainerLocation.CheckContainerLocationPut():
                    # 将主调度设备调度状态改为避让状态
                    MainShaduleDevice.StateCode = -2
                    MainShaduleDeviceWorkFeasibility = False
                else:
                    # 恢复主调度设备作业状态
                    MainShaduleDevice.StateCode = 0
                    MainShaduleDeviceWorkFeasibility = True
        # 获取主调度设备的预计作业区间
        MainShaduleDeviceMoveRange = None
        if MainShaduleDevice.StateCode == 0:
            MainShaduleDeviceMoveRange = Get_MoveRange(MainShaduleDevice.CurrentPos, MainShaduleDevice.TargetPos)
        elif MainShaduleDevice.StateCode == -1:
            MainShaduleDeviceMoveRange = Get_MoveRange(MainShaduleDevice.CurrentPos, MainShaduleDevice.AvoidancePos)
        elif MainShaduleDevice.StateCode == -2:
            MainShaduleDeviceMoveRange = Get_MoveRange(MainShaduleDevice.CurrentPos)
        # 获取被调度设备的正在作业区间
        BeShaduleDeviceMoveRange = None
        if BeShaduleDevice.StateCode == 0 or BeShaduleDevice.StateCode == 1 or BeShaduleDevice.StateCode == 3 \
                or BeShaduleDevice.StateCode == 5:
            BeShaduleDeviceMoveRange = Get_MoveRange(BeShaduleDevice.CurrentPos,
                                                     BeShaduleDevice.TargetPos)
        elif BeShaduleDevice.StateCode == 2 or BeShaduleDevice.StateCode == 4 or BeShaduleDevice.StateCode == -2:
            BeShaduleDeviceMoveRange = Get_MoveRange(BeShaduleDevice.CurrentPos)
        elif BeShaduleDeviceStateCode == -1:
            BeShaduleDeviceMoveRange = Get_MoveRange(BeShaduleDevice.CurrentPos, BeShaduleDevice.AvoidancePos)
        min_val = 0
        max_val = 65
        # 判断主调度设备作业优先级是否大于被调度设备
        if MainShaduleDevice.StateCode == 0 and MainShaduleDevice.LoadOn == True:
            # 获得当前贝位与目标贝位的距离差并归一化
            Distance = abs(MainShaduleDevice.CurrentPos[0] - MainShaduleDevice.TargetPos[0])
            alpha = 0.5 - (0.5 * (Distance - min_val) / (max_val - min_val))
            MainShaduleDevicePriority = PRIORITY.index(MainShaduleDevice.StateCode) + 0.5 + alpha
        else:
            Distance = abs(MainShaduleDevice.CurrentPos[0] - MainShaduleDevice.TargetPos[0])
            alpha = 0.5 - (0.5 * (Distance - min_val) / (max_val - min_val))
            MainShaduleDevicePriority = PRIORITY.index(MainShaduleDevice.StateCode) + alpha
        if BeShaduleDevice.StateCode == 0 and BeShaduleDevice.LoadOn == True:
            Distance = abs(BeShaduleDevice.CurrentPos[0] - BeShaduleDevice.TargetPos[0])
            alpha = 0.5 - (0.5 * (Distance - min_val) / (max_val - min_val))
            BeShaduleDevicePriority = PRIORITY.index(BeShaduleDevice.StateCode) + 0.5 + alpha
        else:
            Distance = abs(BeShaduleDevice.CurrentPos[0] - BeShaduleDevice.TargetPos[0])
            alpha = 0.5 - (0.5 * (Distance - min_val) / (max_val - min_val))
            BeShaduleDevicePriority = PRIORITY.index(BeShaduleDevice.StateCode) + alpha
        if MainShaduleDevicePriority > BeShaduleDevicePriority:
            PriorityFlag = True
        elif MainShaduleDevicePriority == BeShaduleDevicePriority and MainShaduleDevice == Crane:
            PriorityFlag = True
        else:
            PriorityFlag = False
        # 判断主调度设备与被调度设备是否交叉
        if ReachStacker.Worklinepos == WorkLinePos.Down and len(
                np.intersect1d(MainShaduleDeviceMoveRange, BeShaduleDeviceMoveRange)) > 1:
            CrossFlag = True
        else:
            CrossFlag = False

        """---------------------------------------执行作业-------------------------------------------------------"""
        # if MainShaduleDeviceWorkFeasibility is False:
        #     MainShaduleDevice.WaitEvent.succeed()
        #     return True
        # 主调度设备优先级高且两设备不存在路径重叠情形，主调度设备直接作业
        if PriorityFlag == True and CrossFlag == False:
            MainShaduleDevice.MoveEvent.succeed()
            MainShaduleDevice.MoveEvent = env.event()
        # 主调度设备优先级低且两设备不存在路径重叠情形，主调度设备直接作业
        if PriorityFlag == False and CrossFlag == False and MainShaduleDeviceWorkFeasibility is True:
            MainShaduleDevice.MoveEvent.succeed()
            MainShaduleDevice.MoveEvent = env.event()
        if PriorityFlag == False and CrossFlag == False and MainShaduleDeviceWorkFeasibility is False:
            MainShaduleDevice.WaitEvent.succeed()
            MainShaduleDevice.WaitEvent = env.event()
        # 主调度设备优先级高且两设备存在路径重叠情形，被调度设备作业中断且进行避让，主调度设备优先作业
        if PriorityFlag == True and CrossFlag == True and BeShaduleDevice.DeviceLock == True:
            MainShaduleDevice.MoveEvent.succeed()
            MainShaduleDevice.MoveEvent = env.event()
        elif PriorityFlag == True and CrossFlag == True and BeShaduleDevice.DeviceLock == False:
            MainShaduleDevice.MoveEvent.succeed()
            MainShaduleDevice.MoveEvent = env.event()
            # 判断被动调度设备是否需要避让，即被动调度设备当前位置是否在主调度设备的预计作业区间内
            if BeShaduleDevice.CurrentPos[0] in MainShaduleDeviceMoveRange:
                if BeShaduleDevice == Crane:
                    Avoidanceloc = [np.max(MainShaduleDeviceMoveRange) + 1, BeShaduleDevice.CurrentPos[1]]
                else:
                    Avoidanceloc = [np.min(MainShaduleDeviceMoveRange) - 1, BeShaduleDevice.CurrentPos[1]]
                ControllStation.Avoidance(BeShaduleDevice, Avoidanceloc)
            else:
                ControllStation.Wait(BeShaduleDevice)

        # 主调度设备优先级低且两设备存在路径重叠情形，判断主调度设备是否碍事，若碍事则避让，否则原地等待
        if PriorityFlag == False and CrossFlag == True and MainShaduleDevice.DeviceLock == True:
            pass
        elif PriorityFlag == False and CrossFlag == True and MainShaduleDevice.DeviceLock == False:
            CrossRange = np.intersect1d(MainShaduleDeviceMoveRange, BeShaduleDeviceMoveRange)
            if MainShaduleDevice.CurrentPos[0] in CrossRange:
                if MainShaduleDevice == Crane:
                    Avoidanceloc = [np.max(BeShaduleDeviceMoveRange) + 1, MainShaduleDevice.CurrentPos[1]]
                else:
                    Avoidanceloc = [np.min(BeShaduleDeviceMoveRange) - 1, MainShaduleDevice.CurrentPos[1]]
                ControllStation.Avoidance(MainShaduleDevice, Avoidanceloc)
            else:
                ControllStation.Wait(MainShaduleDevice)

    @staticmethod
    def Avoidance(obj, loc):
        "避让作业"
        obj.InterruptWorkEvent.succeed()
        obj.InterruptWorkEvent = env.event()
        # 避让到指定位置
        obj.AvoidancePos = loc
        obj.AvoidanceEvent.succeed()
        obj.AvoidanceEvent = env.event()
        obj.DeviceLock = True
        return True

    @staticmethod
    def Wait(obj):
        "等待作业"
        obj.InterruptWorkEvent.succeed()
        obj.InterruptWorkEvent = env.event()
        obj.WaitEvent.succeed()
        obj.WaitEvent = env.event()
        obj.DeviceLock = True
        return True

    def main(self):
        global ShaduleEvent
        CraneShaduleCount = 1
        ReachStackerShaduleCount = 1
        while True:
            "----------------------------任务分配-----------------------------"
            # 获取轨道吊相关信息
            CraneInfo = Crane.GetInfo("Crane")
            # 获取正面吊相关信息
            ReachStackerInfo = ReachStacker.GetInfo("ReachStacker")
            if CraneInfo["CurrentTask"] == {} and CraneTaskQueue != [] and CraneInfo["StateCode"] == -2:
                # Crane.CurrentTask = CraneTaskQueue.pop(0)
                Crane.CurrentTask = CraneTaskQueue[0]
                Crane.StateCode = 0
                CraneShaduleCount += 1
                self.ShaduleDevice.append(Crane)
            elif CraneInfo["CurrentTask"] == {} and CraneTaskQueue == []:
                Crane.StateCode = -2
            if ReachStackerInfo["CurrentTask"] == {} and ReachStackerTaskQueue != [] and ReachStackerInfo[
                "StateCode"] == -2:
                # ReachStacker.CurrentTask = ReachStackerTaskQueue.pop(0)
                ReachStacker.CurrentTask = ReachStackerTaskQueue[0]
                ReachStacker.StateCode = 0
                ReachStackerShaduleCount += 1
                self.ShaduleDevice.append(ReachStacker)
            elif ReachStackerInfo["CurrentTask"] == {} and ReachStackerTaskQueue == []:
                ReachStacker.StateCode = -2
            for Device in self.ShaduleDevice:
                self.Schedule(Device)
                print("{}调度，当前设备状态为{}".format(Device.name, Device.StateCode))
            self.ShaduleDevice.clear()
            "----------------------------仿真结束条件-----------------------------"
            # 获取轨道吊相关信息
            CraneInfo = Crane.GetInfo("Crane")
            # 获取正面吊相关信息
            ReachStackerInfo = ReachStacker.GetInfo("ReachStacker")
            if CraneTaskQueue == [] and ReachStackerTaskQueue == [] and CraneInfo["CurrentTask"] == {} and \
                    ReachStackerInfo["CurrentTask"] == {}:
                SimulationStopEvent.succeed()
            else:
                yield ShaduleEvent
                Device = ShaduleEvent.value
                if Device.CurrentTask != {}:
                    self.ShaduleDevice.append(ShaduleEvent.value)
                ShaduleEvent = env.event()


"----------------------------正面吊类-----------------------------"


class ReachStacker(simpy.Resource):
    ReachtSackerName = dict()

    def __init__(self, env, name):
        super().__init__(env)
        self.name = name
        self.env = env
        self.CurrentTask = {}  # 记录当前作业的任务
        self.CurrentPos = [0, 6]  # 记录正面吊当前位置
        self.AvoidancePos = []  # 记录避让位置
        self.StateCode = -2
        self.GANTRY_SPEED = 3  # 移动一个贝位所需要的时间
        self.TROLLEY_SPEED = 3  # 移动一个栈位所需要的时间
        self.PICK_TIME = 50
        self.PUT_TIME = 50
        self.Safe_Distance = SafetyDistance  # 安全距离
        self.TargetPos = [0, 6]
        self.EnterPos = []
        self.InterruptFlag = False
        self.PosRegister = []  # 记录正面吊大车位置
        self.Worklinepos = WorkLinePos.Down
        self.DataStack = []
        ReachStacker.ReachtSackerName[self.name] = self

        "----------------------------标志位定义-----------------------------"
        self.GantryMoveingFlag = False
        self.TrolleyMoveingFlag = False
        self.DeviceLock = False  # 设备锁
        self.LoadOn = False  # 负载状态

        "----------------------------事件定义-----------------------------"
        self.GantryMoveEvent = self.env.event()  # 大车开始移动事件
        self.GantryMoveOverEvent = self.env.event()  # 大车移动结束事件
        self.TrolleyMoveEvent = self.env.event()  # 小车开始移动事件
        self.TrolleyMoveOverEvent = self.env.event()  # 小车移动结束事件
        self.MoveEvent = self.env.event()  # 开始移动事件
        self.PickEvent = self.env.event()  # 抓取事件
        self.PutEvent = self.env.event()  # 放置事件
        self.AvoidanceEvent = self.env.event()  # 避让事件
        self.AvoidanceOverEvent = self.env.event()  # 避让完成事件
        self.WaitEvent = self.env.event()  # 等待事件
        self.ReplaceWorkLineEvent = self.env.event()  # 更换作业线事件
        self.ReplaceWorkLineOverEvent = self.env.event()  # 更换作业线完成事件
        self.InterruptWorkEvent = self.env.event()
        self.GantryMoveProcessInterruptOver = self.env.event()
        self.TrolleyMoveProcessInterruptOver = self.env.event()

        "----------------------------进程定义-----------------------------"
        self.GantryMoveProcess = self.env.process(self.GantryMove())
        self.TrolleyMoveProcess = self.env.process(self.TrolleyMove())
        self.env.process(self.Move())
        self.env.process(self.Pick())
        self.env.process(self.Put())
        self.env.process(self.AvoidanceMove())
        self.env.process(self.Wait())
        self.env.process(self.LocationRecord())
        self.env.process(self.ReplaceWorkLine())
        self.env.process(self.InterruptWork())

    def LocationRecord(self):
        "记录位置"
        while True:
            yield self.env.timeout(1)
            self.PosRegister.append([self.CurrentPos[0], self.CurrentPos[1], self.StateCode, self.env.now])

    @classmethod
    def GetInfo(cls, name):
        reachtSacker = cls.ReachtSackerName[name]
        infodict = dict()
        infodict['StateCode'] = reachtSacker.StateCode
        infodict['CurrentPos'] = reachtSacker.CurrentPos
        infodict['CurrentTask'] = reachtSacker.CurrentTask
        return infodict

    def AvoidanceMove(self):
        while True:
            yield self.AvoidanceEvent
            self.AvoidanceEvent = self.env.event()  # 避让事件重置
            # 保留当前作业状态
            StateCode = self.StateCode
            # 将作业状态设为-1，表示避让
            self.StateCode = -1
            self.TargetPos = self.AvoidancePos
            self.GantryMoveEvent.succeed()
            self.GantryMoveEvent = self.env.event()
            self.TrolleyMoveEvent.succeed()
            self.TrolleyMoveEvent = self.env.event()
            yield self.GantryMoveOverEvent & self.TrolleyMoveOverEvent
            # 将作业状态设为有任务空闲状态
            self.StateCode = 0
            while True:
                try:
                    ShaduleEvent.succeed(self)
                    break
                except:
                    yield env.timeout(1)
            self.DeviceLock = False

    def ReplaceWorkLine(self):
        while True:
            yield self.ReplaceWorkLineEvent
            self.ReplaceWorkLineEvent = self.env.event()
            # 将作业状态设为5，表示换线
            self.StateCode = 5
            if self.CurrentPos[1] == 5 or self.CurrentPos[1] == 6:
                self.TargetPos = [0, 14]
            elif self.CurrentPos[1] == 13 or self.CurrentPos[1] == 14:
                self.TargetPos = [0, 5]
            self.GantryMoveEvent.succeed()
            yield self.GantryMoveOverEvent
            self.TrolleyMoveEvent.succeed()
            yield self.TrolleyMoveOverEvent
            self.Worklinepos = WorkLinePos.Down if self.Worklinepos == WorkLinePos.Up else WorkLinePos.Up
            self.ReplaceWorkLineOverEvent.succeed()
            self.ReplaceWorkLineOverEvent = self.env.event()
            self.TargetPos = ControllStation.DataStack.pop()
            self.StateCode = 0
            while True:
                try:
                    ShaduleEvent.succeed(self)
                    break
                except:
                    yield env.timeout(1)

    def InterruptWork(self):
        while True:
            yield self.InterruptWorkEvent
            if self.GantryMoveingFlag:
                self.GantryMoveProcess.interrupt()
                yield self.GantryMoveProcessInterruptOver
                self.GantryMoveProcessInterruptOver = self.env.event()
            if self.TrolleyMoveingFlag:
                self.TrolleyMoveProcess.interrupt()
                yield self.TrolleyMoveProcessInterruptOver
                self.TrolleyMoveProcessInterruptOver = self.env.event()

    def Move(self):
        while True:
            # 抛出开始移动事件，等待事件触发
            yield self.MoveEvent
            # 移动开始事件触发
            # 移动开始事件复位
            self.MoveEvent = self.env.event()
            # 判断当前作业状态，如果作业状态为等待调度状态且没有装载货物，进入下面程序块
            if self.StateCode == 0 and self.LoadOn == False:
                # 将作业状态设为1，表示正在空载移动
                self.StateCode = 1
                # 大车移动开始事件触发
                self.GantryMoveEvent.succeed()
                # 大车移动开始事件复位
                self.GantryMoveEvent = self.env.event()
                # 等待大车移动结束事件触发
                yield self.GantryMoveOverEvent
                # 异常处理，如果遇到中断事件，将中断标志位设为True，跳出循环
                if self.InterruptFlag:
                    self.InterruptFlag = False
                    self.MoveEvent = self.env.event()
                    continue
                # 记录进入通道的位置
                self.EnterPos = copy.deepcopy(self.CurrentPos)
                # 小车移动开始事件触发
                self.TrolleyMoveEvent.succeed()
                # 小车移动开始事件复位
                self.TrolleyMoveEvent = self.env.event()
                # 等待小车移动结束事件触发
                yield self.TrolleyMoveOverEvent
                # 异常处理，如果遇到中断事件，将中断标志位设为True，跳出循环
                if self.InterruptFlag:
                    self.InterruptFlag = False
                    self.MoveEvent = self.env.event()
                    continue
                # 抓取事件触发
                self.PickEvent.succeed()
                # 抓取事件复位
                self.PickEvent = self.env.event()

            # 判断当前作业状态，如果作业状态为等待调度状态且装载货物，进入下面程序块
            if self.StateCode == 0 and self.LoadOn == True:
                # 将作业状态设为3，表示正在重载移动
                self.StateCode = 3
                # 大车移动开始事件触发
                self.GantryMoveEvent.succeed()
                # 大车移动开始事件复位
                self.GantryMoveEvent = self.env.event()
                # 等待大车移动结束事件触发
                yield self.GantryMoveOverEvent
                # 异常处理，如果遇到中断事件，将中断标志位设为True，跳出循环
                if self.InterruptFlag:
                    self.InterruptFlag = False
                    self.MoveEvent = self.env.event()
                    self.StateCode = 0
                    continue
                # 记录进入通道的栈位
                self.EnterPos = copy.deepcopy(self.CurrentPos)
                # 小车移动开始事件触发
                self.TrolleyMoveEvent.succeed()
                # 小车移动开始事件复位
                self.TrolleyMoveEvent = self.env.event()
                # 等待小车移动结束事件触发
                yield self.TrolleyMoveOverEvent
                # 异常处理，如果遇到中断事件，将中断标志位设为True，跳出循环
                if self.InterruptFlag:
                    self.InterruptFlag = False
                    self.MoveEvent = self.env.event()
                    self.StateCode = 0
                    continue
                # 放置事件触发
                self.PutEvent.succeed()
                # 放置事件复位
                self.PutEvent = self.env.event()

    def GantryMove(self):
        while True:
            # 抛出大车移动就绪事件，等待事件触发
            yield self.GantryMoveEvent
            # 大车移动就绪事件复位
            self.GantryMoveEvent = self.env.event()
            while True:
                self.GantryMoveingFlag = True
                if self.CurrentPos[0] == self.TargetPos[0]:
                    break
                else:
                    if self.CurrentPos[0] < self.TargetPos[0]:
                        # 获取轨道吊当前位置
                        CranePos = Crane.CurrentPos
                        # 判断安全距离内是否有轨道吊，若是则原地等待，否则移动一个贝位
                        while True:
                            if CranePos[0] == self.CurrentPos[
                                0] + self.Safe_Distance and self.Worklinepos == WorkLinePos.Down:
                                yield self.env.timeout(1)
                            else:
                                break
                        # 移动
                        try:
                            yield self.env.timeout(self.GANTRY_SPEED)
                        except simpy.Interrupt:
                            self.InterruptFlag = True
                            self.GantryMoveProcessInterruptOver.succeed()
                            break
                        self.CurrentPos[0] += 1
                        print("正面吊大车移动到贝位{}".format(self.CurrentPos[0]))
                        print("当前时间为{}".format(self.env.now))
                    else:
                        # 获取轨道吊当前位置
                        CranePos = Crane.CurrentPos
                        # 判断安全距离内是否有轨道吊，若是则原地等待，否则移动一个贝位
                        while True:
                            if CranePos[0] == self.CurrentPos[
                                0] - self.Safe_Distance and self.Worklinepos == WorkLinePos.Down:
                                yield self.env.timeout(1)
                            else:
                                break
                        try:
                            yield self.env.timeout(self.GANTRY_SPEED)
                        except simpy.Interrupt:
                            self.InterruptFlag = True
                            self.GantryMoveProcessInterruptOver.succeed()
                            break
                        self.CurrentPos[0] -= 1
                        print("正面吊大车移动到贝位{}".format(self.CurrentPos[0]))
                        print("当前时间为{}".format(self.env.now))
            self.GantryMoveingFlag = False
            self.GantryMoveOverEvent.succeed()
            self.GantryMoveOverEvent = self.env.event()

    def TrolleyMove(self):
        while True:
            # 抛出小车移动就绪事件，等待事件触发
            yield self.TrolleyMoveEvent
            self.TrolleyMoveEvent = self.env.event()
            while True:
                self.TrolleyMoveingFlag = True
                if self.CurrentPos[1] == self.TargetPos[1]:
                    break
                else:
                    if self.CurrentPos[1] < self.TargetPos[1]:
                        try:
                            yield self.env.timeout(self.TROLLEY_SPEED)
                        except simpy.Interrupt:
                            self.InterruptFlag = True
                            self.TrolleyMoveProcessInterruptOver.succeed()
                            break
                        self.CurrentPos[1] += 1
                        print("正面吊小车移动到栈位{}".format(self.CurrentPos[1]))
                        print("当前时间为{}".format(self.env.now))
                    else:
                        try:
                            yield self.env.timeout(self.TROLLEY_SPEED)
                        except simpy.Interrupt:
                            self.InterruptFlag = True
                            self.TrolleyMoveProcessInterruptOver.succeed()
                            break
                        self.CurrentPos[1] -= 1
                        print("正面吊小车移动到栈位{}".format(self.CurrentPos[1]))
                        print("当前时间为{}".format(self.env.now))
            self.TrolleyMoveingFlag = False
            self.TrolleyMoveOverEvent.succeed()
            self.TrolleyMoveOverEvent = self.env.event()

    def Pick(self):
        while True:
            # 抛出抓取作业事件，等待事件触发
            yield self.PickEvent
            # 抓取作业事件触发
            PickUpContainerLocation = ContainerLocation.FindContainerLocation(self.CurrentTask['StartPostion'][0],
                                                                              self.CurrentTask['StartPostion'][1],
                                                                              self.CurrentTask['StartPostion'][2]).pop()
            # 进入抓取集装箱状态
            self.StateCode = 2
            # 模拟抓取时间
            yield self.env.timeout(self.PICK_TIME)
            # 进入运输集装箱状态
            self.LoadOn = True
            # 集装箱箱位状态改变
            PickUpContainerLocation.state = 0
            HTArray = np.zeros((2, 14, 66))
            for object_ in ContainerLocation.ContainerList:
                HTArray[object_.level - 1, object_.stack - 1, object_.bay - 1] = object_.state
            DrawPicture.ContainerChangeLocationList.append(copy.deepcopy(HTArray))
            DrawPicture.ContainerChangeLocationTimeList.append(env.now)
            self.StateCode = 3
            # 返回通道入口
            self.TargetPos = self.EnterPos
            self.GantryMoveEvent.succeed()
            self.GantryMoveEvent = self.env.event()
            yield self.GantryMoveOverEvent
            self.TrolleyMoveEvent.succeed()
            self.TrolleyMoveEvent = self.env.event()
            yield self.TrolleyMoveOverEvent
            # 重置进入通道位置
            self.EnterPos = []
            # 进入等待调度状态
            self.StateCode = 0
            while True:
                try:
                    ShaduleEvent.succeed(self)
                    break
                except:
                    yield env.timeout(1)

    def Put(self):
        while True:
            yield self.PutEvent
            PutContainerLocation = ContainerLocation.FindContainerLocation(self.CurrentTask['EndPostion'][0],
                                                                           self.CurrentTask['EndPostion'][1],
                                                                           self.CurrentTask['EndPostion'][2]).pop()
            # 进入放置集装箱状态
            self.StateCode = 4
            # 模拟放置时间
            yield self.env.timeout(self.PUT_TIME)
            # 放置集装箱
            self.LoadOn = False
            # 集装箱箱位状态改变
            PutContainerLocation.state = 1
            HTArray = np.zeros((2, 14, 66))
            for object_ in ContainerLocation.ContainerList:
                HTArray[object_.level - 1, object_.stack - 1, object_.bay - 1] = object_.state
            DrawPicture.ContainerChangeLocationList.append(copy.deepcopy(HTArray))
            DrawPicture.ContainerChangeLocationTimeList.append(env.now)
            self.StateCode = 1
            # 返回通道入口
            self.TargetPos = self.EnterPos
            self.GantryMoveEvent.succeed()
            self.GantryMoveEvent = self.env.event()
            yield self.GantryMoveOverEvent
            self.TrolleyMoveEvent.succeed()
            self.TrolleyMoveEvent = self.env.event()
            yield self.TrolleyMoveOverEvent
            self.EnterPos = []
            # 进入空闲状态
            self.StateCode = -2
            # 任务完成，清空任务
            self.CurrentTask.clear()
            self.TargetPos = self.CurrentPos
            ReachStackerTaskQueue.pop(0)
            # 下一次调度
            while True:
                try:
                    ShaduleEvent.succeed(self)
                    break
                except:
                    yield env.timeout(1)

    def Wait(self):
        while True:
            yield self.WaitEvent
            # 重置等待事件
            self.WaitEvent = self.env.event()
            self.StateCode = 0
            # 进入等待状态
            yield self.env.timeout(5)
            # 等待调度
            while True:
                try:
                    ShaduleEvent.succeed(self)
                    break
                except:
                    yield env.timeout(1)
            self.DeviceLock = False


"----------------------------轨道吊类-----------------------------"


class Crane(simpy.Resource):
    CraneName = dict()

    def __init__(self, env, name):
        super().__init__(env)
        self.name = name
        self.env = env
        self.CurrentTask = {}  # 记录轨道吊当前任务
        self.CurrentPos = [18, 0]  # 记录轨道吊当前位置
        self.AvoidancePos = []  # 记录轨道吊避让位置
        self.StateCode = -2
        self.GANTRY_SPEED = 8  # 移动一个贝位所需要的时间
        self.TROLLEY_SPEED = 6  # 移动一个栈位所需要的时间
        self.PICK_TIME = 50  # 抓取集装箱的所需时间
        self.PUT_TIME = 50  # 放置集装箱的所需时间
        self.Safe_Distance = SafetyDistance  # 安全距离
        self.TargetPos = [18, 0]  # 移动目标位置
        self.PosRegister = []  # 记录轨道吊大车位置
        Crane.CraneName[self.name] = self

        # 标志位定义
        self.GantryMoveingFlag = False  # 大车正在移动标志位
        self.TrolleyMoveingFlag = False  # 小车正在移动标志位
        self.InterruptFlag = False  # 中断标志位
        self.DeviceLock = False  # 设备锁
        self.LoadOn = False  # 负载状态

        # 事件定义
        self.GantryMoveEvent = self.env.event()  # 大车开始移动事件
        self.GantryMoveOverEvent = self.env.event()  # 大车移动结束事件
        self.TrolleyMoveEvent = self.env.event()  # 小车开始移动事件
        self.TrolleyMoveOverEvent = self.env.event()  # 小车移动结束事件
        self.MoveEvent = self.env.event()  # 移动事件
        self.PickEvent = self.env.event()  # 抓取事件
        self.PutEvent = self.env.event()  # 放置事件
        self.AvoidanceEvent = self.env.event()  # 避让事件
        self.AvoidanceOverEvent = self.env.event()  # 避让完成事件
        self.WaitEvent = self.env.event()  # 等待事件
        self.InterruptWorkEvent = self.env.event()
        self.GantryMoveProcessInterruptOver = self.env.event()
        self.TrolleyMoveProcessInterruptOver = self.env.event()

        # 进程定义
        self.GantryMoveProcess = self.env.process(self.GantryMove())
        self.TrolleyMoveProcess = self.env.process(self.TrolleyMove())
        self.env.process(self.Move())
        self.env.process(self.Pick())
        self.env.process(self.Put())
        self.env.process(self.AvoidanceMove())
        self.env.process(self.Wait())
        self.env.process(self.LocationRecord())
        self.env.process(self.InterruptWork())

    def LocationRecord(self):
        "记录轨道吊位置"
        while True:
            yield self.env.timeout(1)
            self.PosRegister.append([self.CurrentPos[0], self.CurrentPos[1], self.StateCode, self.env.now])

    @classmethod
    def GetInfo(cls, name):
        reachtSacker = cls.CraneName[name]
        infodict = dict()
        infodict['StateCode'] = reachtSacker.StateCode
        infodict['CurrentPos'] = reachtSacker.CurrentPos
        infodict['CurrentTask'] = reachtSacker.CurrentTask
        infodict['TargetPos'] = reachtSacker.TargetPos
        return infodict

    def Move(self):
        while True:
            yield self.MoveEvent
            self.MoveEvent = self.env.event()
            if self.StateCode == 0 and self.LoadOn == False:
                self.TargetPos = self.CurrentTask['StartPostion']
                self.StateCode = 1
                self.GantryMoveEvent.succeed()
                self.TrolleyMoveEvent.succeed()
                self.GantryMoveEvent = self.env.event()
                self.TrolleyMoveEvent = self.env.event()
                yield self.GantryMoveOverEvent & self.TrolleyMoveOverEvent
                if self.InterruptFlag:
                    self.InterruptFlag = False
                    self.MoveEvent = self.env.event()
                    self.StateCode = 0
                    continue
                self.PickEvent.succeed()

            if self.StateCode == 0 and self.LoadOn == True:
                self.TargetPos = self.CurrentTask['EndPostion']
                self.StateCode = 3
                self.GantryMoveEvent.succeed()
                self.TrolleyMoveEvent.succeed()
                self.GantryMoveEvent = self.env.event()
                self.TrolleyMoveEvent = self.env.event()
                yield self.GantryMoveOverEvent & self.TrolleyMoveOverEvent
                if self.InterruptFlag:
                    self.InterruptFlag = False
                    self.MoveEvent = self.env.event()
                    self.StateCode = 0
                    continue
                self.PutEvent.succeed()

    def AvoidanceMove(self):
        while True:
            yield self.AvoidanceEvent
            self.AvoidanceEvent = self.env.event()
            # 将作业状态设为-1，表示避让
            self.StateCode = -1
            self.TargetPos = self.AvoidancePos
            self.GantryMoveEvent.succeed()
            self.TrolleyMoveEvent.succeed()
            self.GantryMoveEvent = self.env.event()
            self.TrolleyMoveEvent = self.env.event()
            yield self.GantryMoveOverEvent & self.TrolleyMoveOverEvent
            self.InterruptFlag = False
            # 将作业状态设为有任务空闲状态
            self.StateCode = 0
            # 避让完成,进行下次调度
            while True:
                try:
                    ShaduleEvent.succeed(self)
                    break
                except:
                    yield env.timeout(1)
            self.DeviceLock = False

    def GantryMove(self):
        while True:
            yield self.GantryMoveEvent
            self.GantryMoveEvent = self.env.event()
            while True:
                self.GantryMoveingFlag = True
                if self.CurrentPos[0] == self.TargetPos[0]:
                    break
                else:
                    if self.CurrentPos[0] < self.TargetPos[0]:
                        ReachStackerPos = ReachStacker.CurrentPos
                        while True:
                            if ReachStackerPos[0] == self.CurrentPos[
                                0] + self.Safe_Distance and ReachStacker.Worklinepos == WorkLinePos.Down:
                                yield self.env.timeout(1)
                            else:
                                break
                        try:
                            yield self.env.timeout(self.GANTRY_SPEED)
                        except simpy.Interrupt:
                            self.InterruptFlag = True
                            self.GantryMoveProcessInterruptOver.succeed()
                            break
                        self.CurrentPos[0] += 1
                        print("轨道吊大车移动到贝位{}".format(self.CurrentPos[0]))
                        print("当前时间为{}".format(self.env.now))
                    else:
                        ReachStackerPos = ReachStacker.CurrentPos
                        while True:
                            if ReachStackerPos[0] == self.CurrentPos[
                                0] - self.Safe_Distance and ReachStacker.Worklinepos == WorkLinePos.Down:
                                yield self.env.timeout(1)
                            else:
                                break
                        try:
                            yield self.env.timeout(self.GANTRY_SPEED)
                        except simpy.Interrupt:
                            self.InterruptFlag = True
                            self.GantryMoveProcessInterruptOver.succeed()
                            break
                        self.CurrentPos[0] -= 1
                        print("轨道吊大车移动到贝位{}".format(self.CurrentPos[0]))
                        print("当前时间为{}".format(self.env.now))
            self.GantryMoveingFlag = False
            self.GantryMoveOverEvent.succeed()
            self.GantryMoveOverEvent = self.env.event()

    def TrolleyMove(self):
        while True:
            yield self.TrolleyMoveEvent
            self.TrolleyMoveEvent = self.env.event()
            while True:
                self.TrolleyMoveingFlag = True
                if self.CurrentPos[1] == self.TargetPos[1]:
                    break
                else:
                    if self.CurrentPos[1] < self.TargetPos[1]:
                        try:
                            yield self.env.timeout(self.TROLLEY_SPEED)
                        except simpy.Interrupt:
                            self.InterruptFlag = True
                            self.TrolleyMoveProcessInterruptOver.succeed()
                            break
                        self.CurrentPos[1] += 1
                        print("轨道吊小车移动到栈位{}".format(self.CurrentPos[1]))
                        print("当前时间为{}".format(self.env.now))
                    else:
                        try:
                            yield self.env.timeout(self.TROLLEY_SPEED)
                        except simpy.Interrupt:
                            self.InterruptFlag = True
                            self.TrolleyMoveProcessInterruptOver.succeed()
                            break
                        self.CurrentPos[1] -= 1
                        print("轨道吊小车移动到栈位{}".format(self.CurrentPos[1]))
                        print("当前时间为{}".format(self.env.now))
            self.TrolleyMoveingFlag = False
            self.TrolleyMoveOverEvent.succeed()
            self.TrolleyMoveOverEvent = self.env.event()

    def Pick(self):
        while True:
            yield self.PickEvent
            self.PickEvent = self.env.event()
            # 进入抓取集装箱状态
            self.StateCode = 2
            # 模拟抓取时间
            yield self.env.timeout(self.PICK_TIME)
            self.LoadOn = True
            # 箱位状态调整
            PickContainerLocation = ContainerLocation.FindContainerLocation(self.CurrentTask['StartPostion'][0],
                                                                            self.CurrentTask['StartPostion'][1],
                                                                            self.CurrentTask['StartPostion'][2]).pop()
            PickContainerLocation.state = 0
            HTArray = np.zeros((2, 14, 66))
            for object_ in ContainerLocation.ContainerList:
                HTArray[object_.level - 1, object_.stack - 1, object_.bay - 1] = object_.state
            DrawPicture.ContainerChangeLocationList.append(copy.deepcopy(HTArray))
            DrawPicture.ContainerChangeLocationTimeList.append(env.now)
            self.StateCode = 0
            while True:
                try:
                    ShaduleEvent.succeed(self)
                    break
                except:
                    yield env.timeout(1)

    def Put(self):
        while True:
            yield self.PutEvent
            self.PutEvent = self.env.event()
            # 进入放置集装箱状态
            self.StateCode = 4
            # 模拟放置时间
            yield self.env.timeout(self.PUT_TIME)
            self.LoadOn = False
            PutContainerLocation = ContainerLocation.FindContainerLocation(self.CurrentTask['EndPostion'][0],
                                                                           self.CurrentTask['EndPostion'][1],
                                                                           self.CurrentTask['EndPostion'][2]).pop()
            PutContainerLocation.state = 1
            HTArray = np.zeros((2, 14, 66))
            for object_ in ContainerLocation.ContainerList:
                HTArray[object_.level - 1, object_.stack - 1, object_.bay - 1] = object_.state
            DrawPicture.ContainerChangeLocationList.append(copy.deepcopy(HTArray))
            DrawPicture.ContainerChangeLocationTimeList.append(env.now)
            # 进入空闲状态
            self.StateCode = -2
            self.PutEvent = self.env.event()
            # 任务完成，清空任务
            self.CurrentTask.clear()
            self.TargetPos = self.CurrentPos
            CraneTaskQueue.pop(0)
            # 下一次调度
            while True:
                try:
                    ShaduleEvent.succeed(self)
                    break
                except:
                    yield env.timeout(1)

    def Wait(self):
        while True:
            yield self.WaitEvent
            self.WaitEvent = self.env.event()
            self.StateCode = 0
            # 进入等待状态
            yield self.env.timeout(5)
            # 重置等待事件
            # 等待调度
            while True:
                try:
                    ShaduleEvent.succeed(self)
                    break
                except:
                    yield env.timeout(1)
            self.DeviceLock = False

    def InterruptWork(self):
        while True:
            yield self.InterruptWorkEvent
            if self.GantryMoveingFlag:
                self.GantryMoveProcess.interrupt()
                yield self.GantryMoveProcessInterruptOver
                self.GantryMoveProcessInterruptOver = self.env.event()
            if self.TrolleyMoveingFlag:
                self.TrolleyMoveProcess.interrupt()
                yield self.TrolleyMoveProcessInterruptOver
                self.TrolleyMoveProcessInterruptOver = self.env.event()


"----------------------------主函数-----------------------------"
if __name__ == "__main__":
    DP = DrawPicture()
    ContainerLocationTable = pd.read_excel("箱位状态表(20240325目前可用).xlsx")
    for index, row in ContainerLocationTable.iterrows():
        ContainerLocation(stack=row["栈位"], bay=row["贝位"], level=row["层位"], state=row["占用状态"],
                          Locationtype=row["位置类型"])
    HTArray = np.zeros((2, 14, 66))
    for object_ in ContainerLocation.ContainerList:
        HTArray[object_.level - 1, object_.stack - 1, object_.bay - 1] = object_.state
    InitHTArray = copy.deepcopy(HTArray)
    HTArray = np.zeros((2, 14, 66))
    ReachStacker = ReachStacker(env, "ReachStacker")
    Crane = Crane(env, "Crane")
    ControllStation = ControllStation(env)
    # 计算程序运行时间
    start = time.time()
    env.run(SimulationStopEvent)
    end = time.time()
    DP.LoadData(Crane.PosRegister, ReachStacker.PosRegister, InitHTArray)
    print("程序运行时间为{}".format(end - start))
    plt.show()
    print(Crane.PosRegister)
    print(ReachStacker.PosRegister)
